package com.zhangkang.stock.stocklib.view

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.zhangkang.stock.stocklib.data.KLineInfo
import com.zhangkang.stock.stocklib.tools.dp2px
import com.zhangkang.stock.stocklib.view.index.KLineTurnOver
import com.zhangkang.stock.stocklib.view.index.KLineViewMACD
import com.zhangkang.stock.stocklib.view.index.KLineViewRSI
import com.zhangkang.stock.stocklib.view.index.KLineVolume
import com.zhangkang.stock.stocklib.view.touch.TouchHelper
import kotlin.math.floor

/**
 * @author : Android-张康
 * created on: 2023/5/4 10:59
 * description: CandlestickChartView K线图
 */
class KLineView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr), TouchHelper.OnListener {

    companion object {

        /**
         * 默认展示K线数量
         */
        private const val DEFAULT_K_LINE_NUMBER = 241

        /**
         * 单根K线默认宽度（打开时）：6 单位 dp
         */
        private const val K_LINE_DEFAULT_WIDTH = 6

        /**
         * 单根K线最大宽度：20 单位 dp
         */
        private const val CANDLESTICK_MAX_WIDTH = 20

        /**
         * 单根K线最小宽度：1 单位 dp
         */
        private const val CANDLESTICK_MIN_WIDTH = 1

        /**
         * K线之间的间距：2 单位 dp
         */
        private const val CANDLESTICK_SPACE = 2
    }

    private val mKLineData = mutableListOf<KLineInfo>()

    /**
     * 获取文字信息的 Rect
     */
    private val mTextBoundsRect = Rect()

    /**
     * 蜡烛默认展示的数量
     */
    private var mShowCandlestickNumber = DEFAULT_K_LINE_NUMBER

    /**
     * 蜡烛的最大宽度
     */
    private val candlestickMaxWidth = CANDLESTICK_MAX_WIDTH.dp2px(context)

    /**
     * 蜡烛的最小计算宽度
     */
    internal val candlestickCalculationMinWidth = CANDLESTICK_MIN_WIDTH.dp2px(context)

    /**
     * 蜡烛的最小宽度
     */
    private val candlestickMinWidth = candlestickCalculationMinWidth / 4 * 3

    /**
     * 单个K线绘制宽度
     */
    internal var mCandlestickWidth = K_LINE_DEFAULT_WIDTH.dp2px(context)

    /**
     * 蜡烛之间默认的距离
     */
    private val candlestickDefaultSpace = CANDLESTICK_SPACE.dp2px(context)

    /**
     * K线之间的距离
     */
    internal var mCandlestickSpace = candlestickDefaultSpace


    private var mYangLineIsFill = true

    private var mIsGetWidth = false

    private var mStartIndex = 0

    private var mEndIndex = 0

    private val mKLineViewColor = KLineViewColor(context)

    private val mKLineViewDimen = KLineViewDimen(context)

    /**
     * 线的画笔
     */
    private val mLinePaint: Paint = Paint().apply {
        style = Paint.Style.STROKE
        isAntiAlias = true
        isDither = true
    }

    /**
     * 文字的画笔
     */
    private val mTextPaint = Paint().apply {
        isAntiAlias = true
        isDither = true
    }

    /**
     * K线是否为一条线
     */
    private var mKLineIsLine = false

    /**
     * 缓存Path
     */
    private val mPathPool = PathPool()

    /**
     * 缓存 Rect
     */
    private val mRectPool = RectPool()

    private val mTouchHelper: TouchHelper = TouchHelper(context, this)

    private val indexNameConfig = IndexNameConfig(context)

    private val mIndexInfoListHelper = IndexInfoListHelper(
        pathPool = mPathPool,
        rectPool = mRectPool,
        kLineViewColor = mKLineViewColor,
        kLineViewDimen = mKLineViewDimen,
        indexNameConfig = indexNameConfig
    )

    private val mKLineVolumeList = mutableListOf(
        mIndexInfoListHelper.createKLineIndexInfo(KLineIndex.KLine),
        mIndexInfoListHelper.createKLineIndexInfo(KLineIndex.Volume),
        mIndexInfoListHelper.createKLineIndexInfo(KLineIndex.TurnOver),
        mIndexInfoListHelper.createKLineIndexInfo(KLineIndex.MACD),
        mIndexInfoListHelper.createKLineIndexInfo(KLineIndex.RSI)
    )

    private var mSubplotHeight = 0.0F

    init {
        post {
            calculationDrawNumber()
            calculationMainAndSubplotHeight()
        }
    }

    override fun computeScroll() {
        super.computeScroll()
        mTouchHelper.computeScrollOffset()
    }


    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        return mTouchHelper.onTouchEvent(event)
    }

    override fun onDraw(canvas: Canvas?) {
        canvas ?: return
        if (!mIsGetWidth) {
            return
        }
        val paddingStart = paddingStart.toFloat()
        val paddingEnd = paddingEnd.toFloat()
        val paddingTop = paddingTop.toFloat()
        val paddingBottom = paddingBottom.toFloat()
        val startX = paddingStart
        val stopX = width - paddingEnd
        val startY = paddingTop
        val stopY = height - paddingBottom
        val space = mKLineViewDimen.subplotSpace

        //绘制指标图
        val subplotHeight = mSubplotHeight
        var subplotStartY = startY
        var subplotStopY = subplotStartY
        val lineHeight = mKLineViewDimen.borderLineStrokeWidth
        val currentIndex = mEndIndex
        //绘制指标信息
        val currentKLineInfo: KLineInfo? = if (mKLineData.isEmpty()) {
            null
        } else {
            mKLineData[currentIndex]
        }
        mKLineVolumeList.forEach {
            subplotStopY = subplotStartY + it.getProportion() * subplotHeight + space
            val canvasStartY = subplotStartY + mKLineViewDimen.subplotSpace
            //先绘制背景
            it.drawBackground(
                canvas = canvas,
                startX = startX,
                startY = canvasStartY,
                stopX = stopX,
                stopY = subplotStopY,
                textPaint = mTextPaint,
                linePaint = mLinePaint.apply {
                    strokeWidth = mKLineViewDimen.borderLineStrokeWidth
                    color = mKLineViewColor.borderLineLineColor
                }
            )
            if (mKLineData.isNotEmpty() && null != currentKLineInfo) {
                //绘制当前指标的值
                it.drawCurrentIndexInfo(
                    canvas = canvas,
                    startX = startX,
                    startY = subplotStartY + mKLineViewDimen.borderLineStrokeWidth,
                    stopX = stopX,
                    stopY = canvasStartY - mKLineViewDimen.borderLineStrokeWidth,
                    marginTop = lineHeight,
                    span = 3,
                    indexValueHorizontalSpace = 10.dp2px(context),
                    indexValueVerticalSpace = 10.dp2px(context),
                    kLineInfo = currentKLineInfo,
                    textPaint = mTextPaint,
                    linePaint = mLinePaint,
                    kLineIsLine = mKLineIsLine
                )
            }
            subplotStartY = subplotStopY
        }
        if (mKLineData.isEmpty()) {
            return
        }
        if (null != currentKLineInfo) {
            subplotStartY = startY
            subplotStopY = subplotStartY
            mKLineVolumeList.forEach {
                subplotStopY = subplotStartY + subplotHeight * it.getProportion() + space
                val canvasStartY = subplotStartY + mKLineViewDimen.subplotSpace
                it.beforeDrawItemContent(
                    canvas = canvas,
                    startX = startX,
                    startY = canvasStartY,
                    stopX = stopX,
                    stopY = subplotStopY,
                    paint = mLinePaint,
                    candlestickWidth = mCandlestickWidth,
                    candlestickWidthSpace = mCandlestickSpace,
                    kLineInfo = currentKLineInfo,
                    kLineIsLine = mKLineIsLine
                )
                subplotStartY = subplotStopY
            }
        }
        //蜡烛图的宽度
        val candlestickWidth: Float = if (mKLineIsLine) {
            candlestickCalculationMinWidth
        } else {
            mCandlestickWidth
        }
        val startIndex = mStartIndex
        val endIndex = mEndIndex
        val drawWidth = stopX - startX
        val showKLineNumber = mShowCandlestickNumber
        val dataNumber = endIndex - startIndex + 1
        val drawNumber = Math.min(dataNumber, showKLineNumber)
        //drawNumber == showKLineNumber 说明当前可以绘制满整个屏幕，第一次位移到最右方即可。
        //否则就说明当前蜡烛的数量不足以绘制满整个屏幕，位移（蜡烛图宽度+空隙）*(绘制数量-1)+边框线的宽度
        var totalTranslateX = if (drawNumber == showKLineNumber) {
            drawWidth - mKLineViewDimen.borderLineStrokeWidth - candlestickWidth
        } else {
            (candlestickWidth + mCandlestickSpace) * (drawNumber - 1) + mKLineViewDimen.borderLineStrokeWidth
        }
        val halfCandlestickWidth = candlestickWidth / 2
        val translateX = -mCandlestickSpace - candlestickWidth
        canvas.save()
        canvas.translate(totalTranslateX, 0F)
        var isFirst = true
        for (index in endIndex downTo startIndex) {
            val kLineInfo = mKLineData[index]
            subplotStartY = startY
            subplotStopY = subplotStartY
            mKLineVolumeList.forEach {
                subplotStopY = subplotStartY + subplotHeight * it.getProportion() + space
                val canvasStartY = subplotStartY + mKLineViewDimen.subplotSpace
                it.drawItemContent(
                    canvas = canvas,
                    startX = startX,
                    startY = canvasStartY,
                    stopX = stopX,
                    stopY = subplotStopY,
                    textPaint = mTextPaint,
                    linePaint = mLinePaint,
                    kLineInfo = kLineInfo,
                    isFirst = isFirst,
                    yangLineIsFill = mYangLineIsFill,
                    totalTranslateX = totalTranslateX,
                    candlestickWidth = candlestickWidth,
                    halfCandlestickWidth = halfCandlestickWidth,
                    kLineIsLine = mKLineIsLine
                )
                subplotStartY = subplotStopY
            }
            canvas.translate(translateX, 0F)
            totalTranslateX += translateX
            isFirst = false
        }
        canvas.restore()
        mKLineVolumeList.forEach {
            it.afterDrawItemContent(canvas, mLinePaint.apply {
                style = Paint.Style.STROKE
                strokeWidth = mKLineViewDimen.borderLineStrokeWidth
            })
        }
        //绘制日期
        drawDate(canvas, startX, subplotStopY, stopX, stopY)
    }

    fun setKLineData(klineData: List<KLineInfo>) {
        mKLineData.clear()
        mKLineData.addAll(klineData)
        calculation()
        invalidate()
    }

    /**
     * 计算数据
     */
    private fun calculation() {
        calculationMainAndSubplotHeight()
        //计算绘制范围
        calculationDrawRangeByScale()
        //计算最大值和最小值
        calculationMaxAndMin()
    }

    /**
     * 缩放时，保持结束坐标不变，仅修改开始坐标
     */
    private fun calculationDrawRangeByScale() {
        val oldEndIndex = mEndIndex
        val showCandlestickNumber = mShowCandlestickNumber
        //计算绘制范围
        var startIndex: Int
        var endIndex: Int
        if (mKLineData.size > showCandlestickNumber) {
            endIndex = if (oldEndIndex == 0) {
                mKLineData.size - 1
            } else {
                oldEndIndex
            }
            startIndex = endIndex - showCandlestickNumber + 1
            if (startIndex < 0) {
                startIndex = 0
                endIndex = showCandlestickNumber
            }
        } else {
            startIndex = 0
            endIndex = mKLineData.size - 1
        }
        mStartIndex = startIndex
        mEndIndex = endIndex
    }

    /**
     * 计算主副图的高度
     */
    private fun calculationMainAndSubplotHeight() {
        val drawHeight = height - paddingTop - paddingBottom
        var sum = 0
        mKLineVolumeList.forEach {
            sum += it.getProportion()
        }
        var calculationHeight = drawHeight - mKLineViewDimen.subplotSpace * (sum + 1) * 1.0F
        calculationHeight /= sum
        val subplotHeight = calculationHeight
        mSubplotHeight = subplotHeight
    }

    /**
     * 计算最大值和最小值
     */
    private fun calculationMaxAndMin() {
        if (mKLineData.isEmpty()) {
            return
        }
        val startIndex: Int = mStartIndex
        val endIndex: Int = mEndIndex
        val startKLine = mKLineData[startIndex]
        mKLineVolumeList.forEach {
            it.calculationMaxAndMinBefore(startKLine)
        }
        for (index in startIndex..endIndex) {
            mKLineVolumeList.forEach {
                it.calculationMaxAndMin(mKLineData[index])
            }
        }
        mKLineVolumeList.forEach {
            it.calculationMaxAndMinAfter()
        }
    }

    /**
     * 计算绘制的数量
     *
     * 根据当前屏幕的宽度和蜡烛图的宽度计算
     *
     */
    private fun calculationDrawNumber() {
        val drawWidth =
            width - paddingStart - paddingEnd - 2 * mKLineViewDimen.borderLineStrokeWidth
        val candlestickSpace = candlestickDefaultSpace
        val candlestickWidth = if (mKLineIsLine) {
            candlestickCalculationMinWidth
        } else {
            mCandlestickWidth
        }
        val showCandlestickNumber =
            floor((drawWidth / (candlestickWidth + candlestickSpace)).toDouble()).toInt()
        //重新计算每个蜡烛图之间的间隔
        mCandlestickSpace =
            (drawWidth - (candlestickWidth * showCandlestickNumber)) / (showCandlestickNumber - 1)
        mShowCandlestickNumber = showCandlestickNumber
        mIsGetWidth = true
        invalidate()
    }

    /**
     * 绘制日期
     */
    private fun drawDate(
        canvas: Canvas,
        startX: Float,
        subplotStopY: Float,
        stopX: Float,
        stopY: Float
    ) {
        if (mKLineData.isEmpty()) {
            return
        }
        val startData = mKLineData[mStartIndex]
        val startDate = startData.date.toString()
        mTextPaint.textSize = mKLineViewDimen.ordinateTextSize
        mTextPaint.color = mKLineViewColor.ordinateTextColor
        mTextPaint.getTextBounds(startDate, 0, startDate.length, mTextBoundsRect)
        val startDateY =
            subplotStopY + mTextBoundsRect.height() + mKLineViewDimen.maxOrMinPriceTextPaddingLine
        canvas.drawText(startDate, startX, startDateY, mTextPaint)
        val endData = mKLineData[mEndIndex]
        val endDate = endData.date.toString()
        mTextPaint.textSize = mKLineViewDimen.ordinateTextSize
        mTextPaint.getTextBounds(endDate, 0, startDate.length, mTextBoundsRect)
        val endDateY =
            subplotStopY + mTextBoundsRect.height() + mKLineViewDimen.maxOrMinPriceTextPaddingLine
        val endDateX = stopX - mTextBoundsRect.width()
        canvas.drawText(endDate, endDateX, endDateY, mTextPaint)
    }

    override fun onScale(scaleFactor: Float) {
        mCandlestickWidth *= scaleFactor
        mKLineIsLine = false
        if (mCandlestickWidth > candlestickMaxWidth) {
            mCandlestickWidth = candlestickMaxWidth
        } else if (mCandlestickWidth < candlestickCalculationMinWidth) {
            if (mCandlestickWidth < candlestickMinWidth) {
                mCandlestickWidth = candlestickMinWidth
            }
            mKLineIsLine = true
        }
        //计算绘制的数量
        calculationDrawNumber()
        //计算绘制范围
        calculationDrawRangeByScale()
        //计算最大值和最小值
        calculationMaxAndMin()
        invalidate()
    }

    /**
     * 滚动页面
     *
     * @param scrollX 本次滚动的X
     */
    override fun onScroll(scrollX: Float) {
        var startIndex = mStartIndex
        val maxEnd = mKLineData.size - 1
        //增加灵敏度
        val scrollNumber = (if (scrollX >= 0) {
            scrollX / mCandlestickWidth + 0.75F
        } else {
            scrollX / mCandlestickWidth - 0.75F
        }).toInt()
        startIndex += scrollNumber
        if (startIndex < 0) {
            startIndex = 0
        }
        var endIndex = startIndex + mShowCandlestickNumber
        if (endIndex > maxEnd) {
            endIndex = maxEnd
        }
        //修正开始的位置
        startIndex = endIndex - mShowCandlestickNumber
        if (startIndex < 0) {
            startIndex = 0
        }
        //如果已经滑动到头，停止滑动
        if (startIndex == 0 || endIndex == mKLineData.size - 1) {
            mTouchHelper.forceFinished(true)
        }
        mStartIndex = startIndex
        mEndIndex = endIndex
        calculationMaxAndMin()
        invalidate()
    }

    override fun onShowPress(touchY: Float) {
        val startY = paddingTop
        var subplotStartY = startY.toFloat()
        var subplotStopY: Float
        val subplotHeight = mSubplotHeight
        val space = mKLineViewDimen.subplotSpace
        mKLineVolumeList.forEachIndexed { index, kLineIndexInfo ->
            subplotStopY = subplotStartY + kLineIndexInfo.getProportion() * subplotHeight + space
            if (touchY > subplotStartY && touchY < subplotStopY) {
                mKLineVolumeList[index] = mIndexInfoListHelper.nextIndex(kLineIndexInfo)
                calculationMaxAndMin()
                invalidate()
                return
            }
            subplotStartY = subplotStopY
        }
    }

    override fun getCandlestickWidth(): Float {
        return mCandlestickWidth
    }
}